package cn.wjssl.domain.strategy.service;

import cn.wjssl.domain.strategy.model.entity.RaffleAwardEntity;
import cn.wjssl.domain.strategy.model.entity.RaffleFactorEntity;
import cn.wjssl.domain.strategy.model.entity.StrategyAwardEntity;
import cn.wjssl.domain.strategy.repository.IStrategyRepository;
import cn.wjssl.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.wjssl.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import cn.wjssl.types.enums.ResponseCode;
import cn.wjssl.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.util.Date;

/**
 * 抽奖策略抽象类, 使用模版模式, 在抽象类中定义了 performRaffle 抽奖业务的执行顺序
 */
@Slf4j
public abstract class AbstractRaffleStrategy implements IStrategyService {

    @Resource
    private IStrategyRepository repository;

    @Override
    public RaffleAwardEntity performRaffle(Long strategyId, String userId, Date endDateTime) {
        RaffleFactorEntity raffleFactorEntity = new RaffleFactorEntity();
        raffleFactorEntity.setStrategyId(strategyId);
        raffleFactorEntity.setUserId(userId);
        raffleFactorEntity.setEndDateTime(endDateTime);
        return performRaffle(raffleFactorEntity);
    }


    // 抽奖流程
    @Override
    public RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity) {
        // 1. 首先进行入参校验，对传入的RaffleFactorEntity进行检验不为空
        String userId = raffleFactorEntity.getUserId();
        Long strategyId = raffleFactorEntity.getStrategyId();
        if (null == strategyId || StringUtils.isBlank(userId)) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
        }

        // 2. 前置规则 责任链过滤【这步拿到的是初步的抽奖ID，之后需要根据ID处理抽奖】注意；黑名单、权重等非默认抽奖的直接返回抽奖结果
        DefaultChainFactory.StrategyAwardVO chainStrategyAwardVO = raffleLogicChain(userId, strategyId);
        log.info("抽奖策略计算-责任链 {} {} {} {} {}", userId, strategyId, chainStrategyAwardVO.getAwardId(), chainStrategyAwardVO.getAwardTitle(), chainStrategyAwardVO.getLogicModel());
        // 如果获得的奖品的过滤规则不是默认, 那说明被拦截了, 直接返回奖品对象
        if (!chainStrategyAwardVO.getLogicModel().equals(DefaultChainFactory.LogicModel.RULE_DEFAULT.getCode())) {
            return buildRaffleAwardEntity(strategyId, chainStrategyAwardVO.getAwardId(), chainStrategyAwardVO.getAwardConfig());
        }

        // 3. 规则树抽奖过滤【奖品ID，会根据抽奖次数判断、库存判断、兜底兜里返回最终的可获得奖品信息】
        DefaultTreeFactory.StrategyAwardVO treeStrategyAwardVO = raffleLogicTree(userId, strategyId, chainStrategyAwardVO.getAwardId(), raffleFactorEntity.getEndDateTime());
        log.info("抽奖策略计算-规则树 {} {} {} {}", userId, strategyId, treeStrategyAwardVO.getAwardId(), treeStrategyAwardVO.getAwardRuleValue());

        // 4. 返回抽奖结果, 用内部方法重新封装一下
        return buildRaffleAwardEntity(strategyId, treeStrategyAwardVO.getAwardId(), treeStrategyAwardVO.getAwardRuleValue());
    }

    // 内部方法, 封装返回的奖品实体
    private RaffleAwardEntity buildRaffleAwardEntity(Long strategyId, Integer awardId, String awardConfig) {
        StrategyAwardEntity strategyAward = repository.queryStrategyAwardEntity(strategyId, awardId);
        return RaffleAwardEntity.builder()
                .awardId(awardId)
                .awardConfig(awardConfig)
                .sort(strategyAward.getSort())
                .awardTitle(strategyAward.getAwardTitle())
                .build();
    }
    /**
     * 前置过滤，责任链抽象方法
     *
     * @param userId     用户ID
     * @param strategyId 策略ID
     * @return 奖品ID
     */
    public abstract DefaultChainFactory.StrategyAwardVO raffleLogicChain(String userId, Long strategyId);

    /**
     * 抽奖中过滤，决策树抽象方法
     *
     * @param userId        用户ID
     * @param strategyId    策略ID
     * @param awardId       奖品ID
     * @param endDateTime   过期时间
     * @return
     */
    public abstract DefaultTreeFactory.StrategyAwardVO raffleLogicTree(String userId, Long strategyId, Integer awardId, Date endDateTime);

    /**
     * 抽奖中过滤，决策树抽象方法
     *
     * @param userId     用户ID
     * @param strategyId 策略ID
     * @param awardId    奖品ID
     * @return 过滤结果【奖品ID，会根据抽奖次数判断、库存判断、兜底兜里返回最终的可获得奖品信息】
     */
    public abstract DefaultTreeFactory.StrategyAwardVO raffleLogicTree(String userId, Long strategyId, Integer awardId);
}
